0.1 Required Libraries

library(knitr)
library(data.table)
library(tidyverse)
library(leaflet)
library(lubridate)

0.2 Introduction

  • Observes Trends in the data
  • Correlation Analysis on Temperature and Crime
    The data is found here. This data reflects reported criminal offenses that have occurred in the city of Detroit. Offense data was extracted from the Detroit Police Department’s records management system. ## Loading in the Data Loading the data using readr and read.csv
crime_raw <- read.csv("RMS_Crime_Incidents.csv")

0.3 Data Preview

Using DT

data.table(crime_raw, options = list(pageLength = 5, scrollX = '400px'))
crime_raw %>%
  group_by(year) %>%
  summarize(count = n()) %>% 
  arrange(desc(year))

This dataset has data that goes as far back as 1915 but it only has accurate data since 2017 it appears. The limitation of this data will only allow us to accurately analyze the years 2017 through 2020.

0.4 Processing the data

Renaming the incident_timestamp column for clarity and removing the time from the timestamp because there is already another column with that information. Filtering the data set because of the limitation. Adding extra columns that will allow for easier analysis. Getting rid of columns with the same crime_id and arrest charge because that would mean it was a duplicate entry.

crime <- crime_raw %>% 
  rename(date = incident_timestamp) %>% 
  mutate(date = ymd_hms(date)) %>% 
  mutate(date = date(date)) %>% 
  filter(year > 2016 & year <= 2020) %>% 
  add_column(crime_type = NA) %>% 
  add_column(month = NA) %>% 
  mutate(month = month(date, TRUE)) %>% 
  group_by(crime_id, arrest_charge) %>%
  filter(!n()>1) %>% 
  ungroup()

The two types of crime that are being analyzed are property and violent crimes. In order to categorize these crimes we need to view the unique offense categories.

unique_offenses <- unique(crime[c("offense_category")])
data.table(unique_offenses)

From here we can create two vectors to distinguish the property and violent crimes.

#violent_crime <- scan("violent_crimes.txt", what ="character")
violent_crime <- c("ASSAULT","AGGRAVATED ASSAULT","SEXUAL ASSAULT","HOMICIDE","KIDNAPPING")
property_crime <- c("ROBBERY","LARCENY","ARSON","STOLEN VEHICLE","BURGLARY","DAMAGE TO PROPERTY","STOLEN PROPERTY")

Now we can update the crime_type column to show the crime_type. If it is neither a property or violent crime we will just classify it as other.

`%notin%` <- Negate(`%in%`)
crime <- within(crime, {
  crime_type[offense_category %notin% (violent_crime) || (property_crime)] = "other"
  crime_type[offense_category %in% (violent_crime)] = "violent"
  crime_type[offense_category %in% (property_crime)] = "property"  })

The total of the type of reported crime in the 4 year period.

crime %>% 
  group_by(crime_type) %>% 
  summarize(count = n())
crime %>% 
  filter(crime_type == "other") %>% 
  distinct(offense_category) 

Some of these offenses aren’t dependent on the variables we wanted to analyse. In order to get a clear analysis we will be looking at the data that is either classified as violent or property crime.

crime <- crime %>% 
  filter(crime_type != "other")

0.5 Visualizing the Data

Create a pie chart on the offense category. Here we can see the breakdown of offenses.

dt <- crime %>% 
  group_by(offense_category) %>% 
  summarise(count = n()) %>% 
  arrange(count, .by_group = TRUE)

blank_theme <- theme_minimal()+
  theme(
    axis.title.x = element_blank(),
    axis.title.y = element_blank(),
    panel.border = element_blank(),
    panel.grid=element_blank(),
    axis.ticks = element_blank(),
    plot.title=element_text(size=14, face="bold")
  )
dt %>%  ggplot(aes(x="", y=count, fill= offense_category))+
  geom_bar(width = 1, stat = "identity")+
  coord_polar("y", start = 0)+
  blank_theme +
  theme(axis.text.x=element_blank())+
  labs(title = "Detroit Crime by Offense Category(2017-2020)")

Using the same data but making it bar chart

dt %>% 
  ggplot(aes(x = reorder(offense_category,count), y = count)) +
  geom_col(fill = "orange")+
  coord_flip()+
  labs(title = "Crimes in Detroit 2017-2020", x = "Offense Category", y = "Crime Count")

0.5.1 Crime Map

Display all the offenses in 2020. Click the icons to show details.

data <- crime %>% filter(year == 2020)
data$popup <- paste("<b>Incident #: </b>", data$crime_id, "<br>", "<b>Category: </b>", data$offense_category,
                    "<br>", "<b>Description: </b>", data$offense_description,
                    "<br>", "<b>Day of week: </b>", data$day_of_week,
                    "<br>", "<b>Date: </b>", data$date,
                    "<br>", "<b>Time: </b>", data$incident_time,
                    "<br>", "<b>Neighborhood: </b>", data$neighborhood,
                    "<br>", "<b>Longitude: </b>", data$longitude,
                    "<br>", "<b>Latitude: </b>", data$latitude)

leaflet(data, width = "100%") %>% addTiles() %>%
  addTiles(group = "OSM (default)") %>%
  addProviderTiles(provider = "Esri.WorldStreetMap",group = "World StreetMap") %>%
  #addProviderTiles(provider = "Esri.WorldImagery",group = "World Imagery") %>%
  #addProviderTiles(provider = "NASAGIBS.ViirsEarthAtNight2012",group = "Nighttime Imagery") %>%
  addMarkers(lng = ~longitude, lat = ~latitude, popup = data$popup, clusterOptions = markerClusterOptions()) %>%
  addLayersControl(
    baseGroups = c("OSM (default)","World StreetMap"),
    options = layersControlOptions(collapsed = FALSE)
  )

0.5.2 Crime Type by Time


crime %>%
  group_by(offense_category,hour_of_day) %>%
  summarise(count=n()) %>%
  ggplot(aes(x=offense_category, y=hour_of_day)) +
  geom_tile(aes(fill=count)) +
  labs(Title = "Offense Type by Time", x="Crime", y = "Time of day") +
  scale_fill_gradient(low = "white", high = "#101ade")+
  theme(axis.text.x = element_text(angle = 90, vjust = 0.6), legend.title = element_blank(), legend.position="top", legend.direction="horizontal", legend.key.width=unit(2, "cm"), legend.key.height=unit(0.25, "cm"))+
  coord_flip()
`summarise()` has grouped output by 'offense_category'. You can override using the `.groups` argument.

The data is being shwon properly but due to the low amount of crime in the other categories we can’t see when they are most likely to occur. Lets normalize the data and try again.


crime %>%
  group_by(offense_category,hour_of_day) %>%
  summarise(count=n()) %>%
  mutate(norm = count/sum(count)) %>% 
  ggplot(aes(x=offense_category, y=hour_of_day)) +
  geom_tile(aes(fill=norm)) +
  labs(Title = "Offense Type by Time", x="Crime", y = "Time of day") +
  scale_fill_gradient(low = "white", high = "#101ade")+
  theme(axis.text.x = element_text(angle = 90, vjust = 0.6), legend.title = element_blank(), legend.position="top", legend.direction="horizontal", legend.key.width=unit(2, "cm"), legend.key.height=unit(0.25, "cm"))+
  coord_flip()
`summarise()` has grouped output by 'offense_category'. You can override using the `.groups` argument.

0.5.3 Crime over Time

Lets look at the crime by date to see if there are any trends

crime_daily <- crime %>%
  mutate(date = as.Date(date, "%m/%d/%Y")) %>%
  group_by(date) %>%
  summarize(count = n()) %>%
  arrange(date)

crime_daily %>% 
  ggplot(aes(date, count))+  
  geom_line(color = "orange")+
  geom_smooth(color = "black") 
`geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'

The crime rate appears to raise around the summer months and lowers during the cooler months. Lets take a closer look at this.

crime %>% 
  group_by(month) %>% 
  summarize(count= n()) %>% 
  arrange(month) 

crime_month <- crime %>% 
  group_by(month, year) %>% 
  summarize(count= n()) %>% 
  arrange(month)
`summarise()` has grouped output by 'month'. You can override using the `.groups` argument.
crime_month %>% 
  ggplot(aes(month, count, group = 1))+
  geom_area(fill = "orange")+ 
  scale_y_continuous(expand = c(0, 0), limits = c(0, NA))+
  facet_wrap(~year)

There is a dip in February reported crime , lets try to normalize the data to account for the fewer days.

crime_month <-crime %>% 
  mutate(days_in_month = days_in_month(date)) %>% 
  group_by(month, year, days_in_month) %>% 
  summarize(count= n()) %>% 
  mutate(daily_crime_rate = count/days_in_month)
`summarise()` has grouped output by 'month', 'year'. You can override using the `.groups` argument.
crime_month %>% 
  ggplot(aes(month, daily_crime_rate, group = 1))+
  geom_area(fill = "orange")+
  scale_y_continuous(expand = c(0, 0), limits = c(0, NA))+
  facet_wrap(~year)

From here we can see that accounting for the decreased or increased amount of days in a month by creating a daily crime rate variable we can access the trends more accurately. The crime rate still looks to rise during the warmer months, temperature data is required to analyze the correlation between crime and temperature.

0.6 Crime Vs. Temperature

The National Centers For Environmental Information(NCEI) has historical weather and climate data for the United States. We are using a database that shows the climate data in Detroit taken from the Coleman A. Young International Airport. ### Importing and Joining the Datasets

weather_raw <- read.csv("Detroit_Weather.csv")
data.table(weather_raw)

There is a lot of variables in these observations. We only need the date and the temperature. Dry bulb temperature aka air temperature is defined as the temperature of the air when a thermometer is exposed to it, we will be using this temperature for our analysis.

weather <- weather_raw %>% 
  select(DATE, HourlyDryBulbTemperature) %>%
  rename(temperature = HourlyDryBulbTemperature, date = DATE)
  
weather <- weather %>% mutate(date = ymd_hms(date))
weather$date <- round_date(weather$date, unit="15 minutes") 


weather$hour_of_day <- hour(weather$date)
weather$date <- date(weather$date)
weather <- unique(weather[c("hour_of_day", "date", "temperature")])  
weather <- weather[!(is.na(weather$temperature) | weather$temperature==""), ]
weather$temperature = as.integer(weather$temperature)

data.table(weather)
NA

Formatted and manipulated the data so the columns can match up with the other data frame so we can join. Creating a new variable called hour_of_day and it rounds the time up or down to the nearest 15 minutes.We will be able to analyze what the temperature was the hour the crime was committed.

crime <- left_join(crime, weather, by = c("date", "hour_of_day")) %>%   
  distinct(arrest_charge, offense_category, date, crime_id, .keep_all = TRUE) %>% 
  select(-arrest_charge, -crime_id) %>% 
  fill(temperature)
data.table(crime, options = list(pageLength = 5, scrollX = '400px'))

After joining the tables there were some NA values because some of the values in the weather dataset was blank so we just filled it with the nearest value using the fill function.

crime_temp_month <- crime %>% 
  mutate(pop = case_when(
    year == 2017 ~ 679865,
    year == 2018 ~ 677155,
    year == 2019 ~ 674841,
    year == 2020 ~ 664139
  )) %>% 
  group_by(month, year, pop) %>%
  summarize(mean_temp = mean(temperature, na.rm =T), count = n()) %>% 
  mutate(crime_rate = count/pop*100000)
`summarise()` has grouped output by 'month', 'year'. You can override using the `.groups` argument.
crime_temp_month %>% 
  ggplot(aes(mean_temp, crime_rate)) + 
  geom_point()+
  geom_smooth(color = "orange", method = "lm")+
  labs(title = "Temperature vs. Crime Rate in Detroit(2017-2020)", x = " Average Temperature(Month)", y = "Crime Rate Per 100000")
`geom_smooth()` using formula 'y ~ x'

Here we can see the average temperature of all the months and the crime rate per 100000. We can see a positive correlation between the average temperature and the crime rate.

cor(crime_temp_month$mean_temp, crime_temp_month$crime_rate)
[1] 0.8005123

We can see that the correlation is .8 which is a strong positive correlation between the two variables. ## Crime Vs. Day of Week

hour_format <- c(paste(c(12,1:11),"AM"), paste(c(12,1:11),"PM"))
dow_format <- c("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday")

crime_dow <- crime %>% 
  group_by(day_of_week, offense_category, hour_of_day) %>% 
  summarize(count = n())
`summarise()` has grouped output by 'day_of_week', 'offense_category'. You can override using the `.groups` argument.
crime_dow$day_of_week <- factor(crime_dow$day_of_week, labels = rev(dow_format), ordered = TRUE)
crime_dow$hour_of_day <- factor(crime_dow$hour_of_day, level = 0:23, label = hour_format)


data.table(crime_dow)
ggplot(crime_dow, aes(x = hour_of_day, y = day_of_week, fill = count))+
  geom_tile()+
  theme(axis.text.x = element_text(angle = 90, vjust = 0.6), legend.title = element_blank(), legend.position="top", legend.direction="horizontal", legend.key.width=unit(2, "cm"), legend.key.height=unit(0.25, "cm")) +
  labs(x = "Hour of Reported Crime", y = "Day of Week ", title = "Reported Property and Violent Crimes in Detroit (2017-2020)") +
  scale_fill_gradient(low = "white", high = "#101ade")

Lets look at the correlation between offense category and time.

crime_dow <- crime_dow %>% 
  mutate(norm = count/sum(count))
ggplot(crime_dow, aes(x = hour_of_day, y = day_of_week, fill = norm)) +
  geom_tile() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.6, size = 4)) +
  labs(x = "Hour of Crime", y = "Day of Week", title = "Number of Crimes in Detroit from 2017-2020 by Normalized by Offense") +
  scale_fill_gradient(low = "white", high = "#101ade") +
  facet_wrap(~ offense_category)

Here we can see that a lot of crimes happen late night/early morning.

0.7 Crime by Zipcode

Lets take a look at crime by zip code

Factoring.

crime_dow_zipcode <- crime %>% 
  group_by(day_of_week, zip_code, hour_of_day) %>% 
  summarise(count = n())
`summarise()` has grouped output by 'day_of_week', 'zip_code'. You can override using the `.groups` argument.
crime_dow_zipcode$day_of_week <- factor(crime_dow_zipcode$day_of_week, labels = rev(dow_format), ordered = TRUE)
crime_dow_zipcode$hour_of_day <- factor(crime_dow_zipcode$hour_of_day, level = 0:23, label = hour_format)
ggplot(crime_dow_zipcode, aes(x = hour_of_day, y = day_of_week, fill = count)) +
  geom_tile() +
  theme(axis.text.x = element_text(angle = 90, vjust = 0.6, size = 4)) +
  labs(x = "Hour of Crime", y = "Day of Week", title = "Number of crimes in Detroit 2017 – 2020, by Zipcode") +
  scale_fill_gradient(low = "white", high = "#101ade") +
  facet_wrap(~ zip_code)  

This visualization doesn’t account for crime in zip codes where the population in lower. Lets find some data on population in the area codes so we can look at crime rate.

zip_code_database<- read.csv("detroit_zipcodes.csv")
data.table(zip_code_database)
NA
NA

This data is from https://www.michigan-demographics.com/ which claims to have up to date demographics from the US census.Some of the zip codes in the dataset have other cities in which we dont have crime data for. So those zipcodes will be filtered out so we can look at a majority of Detroit.


filtered_zipcodes <-c(48203, 48212, 48236, 48239, 48243)

crime_zipcode <- crime %>% 
  filter(zip_code %notin% filtered_zipcodes) %>% 
  group_by(zip_code, crime_type) %>% 
  summarise(count = n())
`summarise()` has grouped output by 'zip_code'. You can override using the `.groups` argument.
data.table(zip_code_database)
NA
crime_zipcode <- left_join(crime_zipcode, zip_code_database, by = "zip_code") %>% 
  drop_na() %>% 
  mutate(crime_rate = (count/Population)*1000)
crime_zipcode$zip_code <- factor(crime_zipcode$zip_code)


data.table(crime_zipcode)
ggplot(crime_zipcode, aes(zip_code,crime_rate, fill = crime_type))+
  geom_col() +
  scale_fill_manual("crime_type", values = c("property"= "#101ade", "violent" = "orange")) +
  coord_flip()+
  labs(title = "Crime Rate in Detroit by Zipcode (2017-2020)",x = "Zip Code",
       y = "Crime Rate per 1000",
       fill = "Crime Type")

Here we can see the crime by zip codes and what instantly stands out is the 48226 zip code. This zip code is downtown and has very few residents so the crime rate is inflated. The crime rate is also misleading because downtown Detroit is a hot spot of many attractions and that leads to non residents from all around the Metro Detroit area to visit. There is other zipcodes that are new downtown or midtown I will get exclude from this analysis.

This graph is scaled better and we can see the differences between the other zipcodes.

downtown_zipcodes <- c(48243, 48226, 48201, 48202, 48216, 48207)
crime_zipcode %>% 
  filter(zip_code %notin% downtown_zipcodes) %>% 
  ggplot(aes(zip_code,crime_rate, fill = crime_type))+
  geom_col() +
  scale_fill_manual("crime_type", values = c("property"= "#101ade", "violent" = "orange")) +
  coord_flip()+
  labs(title = "Crime Rate in Detroit by Zipcode* (2017-2020)",
       caption = "* Downtown, Midtown and surrounding areas removed",
       x = "Zip Code",
       y = "Crime Rate per 1000",
       fill = "Crime Type")

0.8 Conclusion

We can see that weather has a positive correlation with crime rate also it seems like the summer months are when the crime rate is the highest and the colder months especially February seeing a decline. Most of the crimes happen during late night/ early morning. We can also see a correlation with Covid-19 and crime since 2020 numbers were lower than the other years. We then took a look at the crime rate by zipcode and looked at the time and day when a certain offense might be committed.

LS0tDQp0aXRsZTogJ0V4cGxvcmF0b3J5IEFuYWx5c2lzOiBEZXRyb2l0IFJlcG9ydGVkIENyaW1lcyAoMjAxNy0yMDIwKSBCeSBLZWl0aCBQYXJpc2gnDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdGhlbWU6IHVuaXRlZA0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQotLS0NCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfSANCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkgDQpgYGANCiMjIFJlcXVpcmVkIExpYnJhcmllcw0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KGRhdGEudGFibGUpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkobGVhZmxldCkNCmxpYnJhcnkobHVicmlkYXRlKQ0KYGBgDQojIyBJbnRyb2R1Y3Rpb24NCiAgICogT2JzZXJ2ZXMgVHJlbmRzIGluIHRoZSBkYXRhDQogICAqIENvcnJlbGF0aW9uIEFuYWx5c2lzIG9uIFRlbXBlcmF0dXJlIGFuZCBDcmltZQ0KICAgPGJyPg0KVGhlIGRhdGEgaXMgZm91bmQgW2hlcmVdKGh0dHBzOi8vZGF0YS5kZXRyb2l0bWkuZ292L2RhdGFzZXRzL3Jtcy1jcmltZS1pbmNpZGVudHMvZXhwbG9yZT9sb2NhdGlvbj00Mi4zNTMwMDIlMkMtODMuMDk5MDM2JTJDMTEuMjkpLiBUaGlzIGRhdGEgcmVmbGVjdHMgcmVwb3J0ZWQgY3JpbWluYWwgb2ZmZW5zZXMgdGhhdCBoYXZlIG9jY3VycmVkICBpbiB0aGUgY2l0eSBvZiBEZXRyb2l0LiBPZmZlbnNlIGRhdGEgd2FzIGV4dHJhY3RlZCBmcm9tIHRoZSBEZXRyb2l0IFBvbGljZSBEZXBhcnRtZW50J3MgcmVjb3JkcyBtYW5hZ2VtZW50IHN5c3RlbS4NCiMjIExvYWRpbmcgaW4gdGhlIERhdGENCkxvYWRpbmcgdGhlIGRhdGEgdXNpbmcgcmVhZHIgYW5kIHJlYWQuY3N2DQpgYGB7cn0NCmNyaW1lX3JhdyA8LSByZWFkLmNzdigiUk1TX0NyaW1lX0luY2lkZW50cy5jc3YiKQ0KYGBgDQogDQojIyBEYXRhIFByZXZpZXcNClVzaW5nIERUIA0KYGBge3J9DQpkYXRhLnRhYmxlKGNyaW1lX3Jhdywgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDUsIHNjcm9sbFggPSAnNDAwcHgnKSkNCmBgYA0KYGBge3J9DQpjcmltZV9yYXcgJT4lDQogIGdyb3VwX2J5KHllYXIpICU+JQ0KICBzdW1tYXJpemUoY291bnQgPSBuKCkpICU+JSANCiAgYXJyYW5nZShkZXNjKHllYXIpKQ0KYGBgDQpUaGlzIGRhdGFzZXQgaGFzIGRhdGEgdGhhdCBnb2VzIGFzIGZhciBiYWNrIGFzIDE5MTUgYnV0IGl0IG9ubHkgaGFzIGFjY3VyYXRlIGRhdGEgc2luY2UgMjAxNyBpdCBhcHBlYXJzLiBUaGUgbGltaXRhdGlvbiBvZiB0aGlzIGRhdGEgd2lsbCBvbmx5IGFsbG93IHVzIHRvIGFjY3VyYXRlbHkgYW5hbHl6ZSB0aGUgeWVhcnMgMjAxNyB0aHJvdWdoIDIwMjAuDQoNCiMjIFByb2Nlc3NpbmcgdGhlIGRhdGENClJlbmFtaW5nIHRoZSBpbmNpZGVudF90aW1lc3RhbXAgY29sdW1uIGZvciBjbGFyaXR5IGFuZCByZW1vdmluZyB0aGUgdGltZSBmcm9tIHRoZSB0aW1lc3RhbXAgYmVjYXVzZSB0aGVyZSBpcyBhbHJlYWR5IGFub3RoZXIgY29sdW1uIHdpdGggdGhhdCBpbmZvcm1hdGlvbi4gRmlsdGVyaW5nIHRoZSBkYXRhIHNldCBiZWNhdXNlIG9mIHRoZSBsaW1pdGF0aW9uLiBBZGRpbmcgZXh0cmEgY29sdW1ucyB0aGF0IHdpbGwgYWxsb3cgZm9yIGVhc2llciBhbmFseXNpcy4gR2V0dGluZyByaWQgb2YgY29sdW1ucyB3aXRoICB0aGUgc2FtZSBjcmltZV9pZCBhbmQgYXJyZXN0IGNoYXJnZSBiZWNhdXNlIHRoYXQgd291bGQgbWVhbiBpdCB3YXMgYSBkdXBsaWNhdGUgZW50cnkuDQoNCmBgYHtyfQ0KY3JpbWUgPC0gY3JpbWVfcmF3ICU+JSANCiAgcmVuYW1lKGRhdGUgPSBpbmNpZGVudF90aW1lc3RhbXApICU+JSANCiAgbXV0YXRlKGRhdGUgPSB5bWRfaG1zKGRhdGUpKSAlPiUgDQogIG11dGF0ZShkYXRlID0gZGF0ZShkYXRlKSkgJT4lIA0KICBmaWx0ZXIoeWVhciA+IDIwMTYgJiB5ZWFyIDw9IDIwMjApICU+JSANCiAgYWRkX2NvbHVtbihjcmltZV90eXBlID0gTkEpICU+JSANCiAgYWRkX2NvbHVtbihtb250aCA9IE5BKSAlPiUgDQogIG11dGF0ZShtb250aCA9IG1vbnRoKGRhdGUsIFRSVUUpKSAlPiUgDQogIGdyb3VwX2J5KGNyaW1lX2lkLCBhcnJlc3RfY2hhcmdlKSAlPiUNCiAgZmlsdGVyKCFuKCk+MSkgJT4lIA0KICB1bmdyb3VwKCkNCg0KYGBgDQpUaGUgdHdvIHR5cGVzIG9mIGNyaW1lIHRoYXQgYXJlIGJlaW5nIGFuYWx5emVkIGFyZSBwcm9wZXJ0eSBhbmQgdmlvbGVudCBjcmltZXMuIEluIG9yZGVyIHRvIGNhdGVnb3JpemUgdGhlc2UgY3JpbWVzIHdlIG5lZWQgdG8gdmlldyB0aGUgdW5pcXVlIG9mZmVuc2UgY2F0ZWdvcmllcy4NCmBgYHtyfQ0KdW5pcXVlX29mZmVuc2VzIDwtIHVuaXF1ZShjcmltZVtjKCJvZmZlbnNlX2NhdGVnb3J5IildKQ0KZGF0YS50YWJsZSh1bmlxdWVfb2ZmZW5zZXMpDQpgYGANCkZyb20gaGVyZSB3ZSBjYW4gY3JlYXRlIHR3byB2ZWN0b3JzIHRvIGRpc3Rpbmd1aXNoIHRoZSBwcm9wZXJ0eSBhbmQgdmlvbGVudCBjcmltZXMuDQpgYGB7cn0NCiN2aW9sZW50X2NyaW1lIDwtIHNjYW4oInZpb2xlbnRfY3JpbWVzLnR4dCIsIHdoYXQgPSJjaGFyYWN0ZXIiKQ0KdmlvbGVudF9jcmltZSA8LSBjKCJBU1NBVUxUIiwiQUdHUkFWQVRFRCBBU1NBVUxUIiwiU0VYVUFMIEFTU0FVTFQiLCJIT01JQ0lERSIsIktJRE5BUFBJTkciKQ0KcHJvcGVydHlfY3JpbWUgPC0gYygiUk9CQkVSWSIsIkxBUkNFTlkiLCJBUlNPTiIsIlNUT0xFTiBWRUhJQ0xFIiwiQlVSR0xBUlkiLCJEQU1BR0UgVE8gUFJPUEVSVFkiLCJTVE9MRU4gUFJPUEVSVFkiKQ0KYGBgDQpOb3cgd2UgY2FuIHVwZGF0ZSB0aGUgY3JpbWVfdHlwZSBjb2x1bW4gdG8gc2hvdyB0aGUgY3JpbWVfdHlwZS4gSWYgaXQgaXMgbmVpdGhlciBhIHByb3BlcnR5IG9yIHZpb2xlbnQgY3JpbWUgd2Ugd2lsbCBqdXN0IGNsYXNzaWZ5IGl0IGFzIG90aGVyLg0KYGBge3J9DQpgJW5vdGluJWAgPC0gTmVnYXRlKGAlaW4lYCkNCmNyaW1lIDwtIHdpdGhpbihjcmltZSwgew0KICBjcmltZV90eXBlW29mZmVuc2VfY2F0ZWdvcnkgJW5vdGluJSAodmlvbGVudF9jcmltZSkgfHwgKHByb3BlcnR5X2NyaW1lKV0gPSAib3RoZXIiDQogIGNyaW1lX3R5cGVbb2ZmZW5zZV9jYXRlZ29yeSAlaW4lICh2aW9sZW50X2NyaW1lKV0gPSAidmlvbGVudCINCiAgY3JpbWVfdHlwZVtvZmZlbnNlX2NhdGVnb3J5ICVpbiUgKHByb3BlcnR5X2NyaW1lKV0gPSAicHJvcGVydHkiICB9KQ0KYGBgDQpUaGUgdG90YWwgb2YgdGhlIHR5cGUgb2YgcmVwb3J0ZWQgY3JpbWUgaW4gdGhlIDQgeWVhciBwZXJpb2QuDQoNCmBgYHtyfQ0KY3JpbWUgJT4lIA0KICBncm91cF9ieShjcmltZV90eXBlKSAlPiUgDQogIHN1bW1hcml6ZShjb3VudCA9IG4oKSkNCmBgYA0KDQpgYGB7cn0NCmNyaW1lICU+JSANCiAgZmlsdGVyKGNyaW1lX3R5cGUgPT0gIm90aGVyIikgJT4lIA0KICBkaXN0aW5jdChvZmZlbnNlX2NhdGVnb3J5KSANCmBgYA0KU29tZSBvZiAgdGhlc2Ugb2ZmZW5zZXMgYXJlbid0IGRlcGVuZGVudCBvbiB0aGUgdmFyaWFibGVzIHdlIHdhbnRlZCB0byBhbmFseXNlLiBJbiBvcmRlciB0byBnZXQgYSBjbGVhciBhbmFseXNpcyB3ZSB3aWxsIGJlIGxvb2tpbmcgYXQgdGhlIGRhdGEgdGhhdCBpcyBlaXRoZXIgY2xhc3NpZmllZCBhcyB2aW9sZW50IG9yIHByb3BlcnR5IGNyaW1lLg0KYGBge3J9DQpjcmltZSA8LSBjcmltZSAlPiUgDQogIGZpbHRlcihjcmltZV90eXBlICE9ICJvdGhlciIpDQpgYGANCg0KDQojIyBWaXN1YWxpemluZyB0aGUgRGF0YQ0KDQpDcmVhdGUgYSBwaWUgY2hhcnQgb24gdGhlIG9mZmVuc2UgY2F0ZWdvcnkuIEhlcmUgd2UgY2FuIHNlZSB0aGUgYnJlYWtkb3duIG9mIG9mZmVuc2VzLg0KYGBge3J9DQpkdCA8LSBjcmltZSAlPiUgDQogIGdyb3VwX2J5KG9mZmVuc2VfY2F0ZWdvcnkpICU+JSANCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUgDQogIGFycmFuZ2UoY291bnQsIC5ieV9ncm91cCA9IFRSVUUpDQoNCmJsYW5rX3RoZW1lIDwtIHRoZW1lX21pbmltYWwoKSsNCiAgdGhlbWUoDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZD1lbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTE0LCBmYWNlPSJib2xkIikNCiAgKQ0KZHQgJT4lICBnZ3Bsb3QoYWVzKHg9IiIsIHk9Y291bnQsIGZpbGw9IG9mZmVuc2VfY2F0ZWdvcnkpKSsNCiAgZ2VvbV9iYXIod2lkdGggPSAxLCBzdGF0ID0gImlkZW50aXR5IikrDQogIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQgPSAwKSsNCiAgYmxhbmtfdGhlbWUgKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpKw0KICBsYWJzKHRpdGxlID0gIkRldHJvaXQgQ3JpbWUgYnkgT2ZmZW5zZSBDYXRlZ29yeSgyMDE3LTIwMjApIikNCmBgYA0KVXNpbmcgdGhlIHNhbWUgZGF0YSBidXQgbWFraW5nIGl0IGJhciBjaGFydA0KYGBge3J9DQpkdCAlPiUgDQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIob2ZmZW5zZV9jYXRlZ29yeSxjb3VudCksIHkgPSBjb3VudCkpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICJvcmFuZ2UiKSsNCiAgY29vcmRfZmxpcCgpKw0KICBsYWJzKHRpdGxlID0gIkNyaW1lcyBpbiBEZXRyb2l0IDIwMTctMjAyMCIsIHggPSAiT2ZmZW5zZSBDYXRlZ29yeSIsIHkgPSAiQ3JpbWUgQ291bnQiKQ0KYGBgDQoNCg0KIyMjIENyaW1lIE1hcA0KRGlzcGxheSBhbGwgdGhlIG9mZmVuc2VzIGluIDIwMjAuIENsaWNrIHRoZSBpY29ucyB0byBzaG93IGRldGFpbHMuDQpgYGB7cn0NCmRhdGEgPC0gY3JpbWUgJT4lIGZpbHRlcih5ZWFyID09IDIwMjApDQpkYXRhJHBvcHVwIDwtIHBhc3RlKCI8Yj5JbmNpZGVudCAjOiA8L2I+IiwgZGF0YSRjcmltZV9pZCwgIjxicj4iLCAiPGI+Q2F0ZWdvcnk6IDwvYj4iLCBkYXRhJG9mZmVuc2VfY2F0ZWdvcnksDQogICAgICAgICAgICAgICAgICAgICI8YnI+IiwgIjxiPkRlc2NyaXB0aW9uOiA8L2I+IiwgZGF0YSRvZmZlbnNlX2Rlc2NyaXB0aW9uLA0KICAgICAgICAgICAgICAgICAgICAiPGJyPiIsICI8Yj5EYXkgb2Ygd2VlazogPC9iPiIsIGRhdGEkZGF5X29mX3dlZWssDQogICAgICAgICAgICAgICAgICAgICI8YnI+IiwgIjxiPkRhdGU6IDwvYj4iLCBkYXRhJGRhdGUsDQogICAgICAgICAgICAgICAgICAgICI8YnI+IiwgIjxiPlRpbWU6IDwvYj4iLCBkYXRhJGluY2lkZW50X3RpbWUsDQogICAgICAgICAgICAgICAgICAgICI8YnI+IiwgIjxiPk5laWdoYm9yaG9vZDogPC9iPiIsIGRhdGEkbmVpZ2hib3Job29kLA0KICAgICAgICAgICAgICAgICAgICAiPGJyPiIsICI8Yj5Mb25naXR1ZGU6IDwvYj4iLCBkYXRhJGxvbmdpdHVkZSwNCiAgICAgICAgICAgICAgICAgICAgIjxicj4iLCAiPGI+TGF0aXR1ZGU6IDwvYj4iLCBkYXRhJGxhdGl0dWRlKQ0KDQpsZWFmbGV0KGRhdGEsIHdpZHRoID0gIjEwMCUiKSAlPiUgYWRkVGlsZXMoKSAlPiUNCiAgYWRkVGlsZXMoZ3JvdXAgPSAiT1NNIChkZWZhdWx0KSIpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVyID0gIkVzcmkuV29ybGRTdHJlZXRNYXAiLGdyb3VwID0gIldvcmxkIFN0cmVldE1hcCIpICU+JQ0KICAjYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlciA9ICJFc3JpLldvcmxkSW1hZ2VyeSIsZ3JvdXAgPSAiV29ybGQgSW1hZ2VyeSIpICU+JQ0KICAjYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlciA9ICJOQVNBR0lCUy5WaWlyc0VhcnRoQXROaWdodDIwMTIiLGdyb3VwID0gIk5pZ2h0dGltZSBJbWFnZXJ5IikgJT4lDQogIGFkZE1hcmtlcnMobG5nID0gfmxvbmdpdHVkZSwgbGF0ID0gfmxhdGl0dWRlLCBwb3B1cCA9IGRhdGEkcG9wdXAsIGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoKSkgJT4lDQogIGFkZExheWVyc0NvbnRyb2woDQogICAgYmFzZUdyb3VwcyA9IGMoIk9TTSAoZGVmYXVsdCkiLCJXb3JsZCBTdHJlZXRNYXAiKSwNCiAgICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpDQogICkNCmBgYA0KIyMjIENyaW1lIFR5cGUgYnkgVGltZQ0KYGBge3J9DQoNCmNyaW1lICU+JQ0KICBncm91cF9ieShvZmZlbnNlX2NhdGVnb3J5LGhvdXJfb2ZfZGF5KSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50PW4oKSkgJT4lDQogIGdncGxvdChhZXMoeD1vZmZlbnNlX2NhdGVnb3J5LCB5PWhvdXJfb2ZfZGF5KSkgKw0KICBnZW9tX3RpbGUoYWVzKGZpbGw9Y291bnQpKSArDQogIGxhYnMoVGl0bGUgPSAiT2ZmZW5zZSBUeXBlIGJ5IFRpbWUiLCB4PSJDcmltZSIsIHkgPSAiVGltZSBvZiBkYXkiKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIiwgaGlnaCA9ICIjMTAxYWRlIikrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC42KSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249InRvcCIsIGxlZ2VuZC5kaXJlY3Rpb249Imhvcml6b250YWwiLCBsZWdlbmQua2V5LndpZHRoPXVuaXQoMiwgImNtIiksIGxlZ2VuZC5rZXkuaGVpZ2h0PXVuaXQoMC4yNSwgImNtIikpKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KVGhlIGRhdGEgaXMgYmVpbmcgc2h3b24gcHJvcGVybHkgYnV0IGR1ZSB0byB0aGUgbG93IGFtb3VudCBvZiBjcmltZSBpbiB0aGUgb3RoZXIgY2F0ZWdvcmllcyB3ZSBjYW4ndCBzZWUgd2hlbiB0aGV5IGFyZSBtb3N0IGxpa2VseSB0byBvY2N1ci4gTGV0cyBub3JtYWxpemUgdGhlIGRhdGEgYW5kIHRyeSBhZ2Fpbi4NCg0KYGBge3J9DQoNCmNyaW1lICU+JQ0KICBncm91cF9ieShvZmZlbnNlX2NhdGVnb3J5LGhvdXJfb2ZfZGF5KSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50PW4oKSkgJT4lDQogIG11dGF0ZShub3JtID0gY291bnQvc3VtKGNvdW50KSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHg9b2ZmZW5zZV9jYXRlZ29yeSwgeT1ob3VyX29mX2RheSkpICsNCiAgZ2VvbV90aWxlKGFlcyhmaWxsPW5vcm0pKSArDQogIGxhYnMoVGl0bGUgPSAiT2ZmZW5zZSBUeXBlIGJ5IFRpbWUiLCB4PSJDcmltZSIsIHkgPSAiVGltZSBvZiBkYXkiKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIiwgaGlnaCA9ICIjMTAxYWRlIikrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC42KSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb249InRvcCIsIGxlZ2VuZC5kaXJlY3Rpb249Imhvcml6b250YWwiLCBsZWdlbmQua2V5LndpZHRoPXVuaXQoMiwgImNtIiksIGxlZ2VuZC5rZXkuaGVpZ2h0PXVuaXQoMC4yNSwgImNtIikpKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQojIyMgQ3JpbWUgb3ZlciBUaW1lDQpMZXRzIGxvb2sgYXQgdGhlIGNyaW1lIGJ5IGRhdGUgdG8gc2VlIGlmIHRoZXJlIGFyZSBhbnkgdHJlbmRzDQoNCmBgYHtyfQ0KY3JpbWVfZGFpbHkgPC0gY3JpbWUgJT4lDQogIG11dGF0ZShkYXRlID0gYXMuRGF0ZShkYXRlLCAiJW0vJWQvJVkiKSkgJT4lDQogIGdyb3VwX2J5KGRhdGUpICU+JQ0KICBzdW1tYXJpemUoY291bnQgPSBuKCkpICU+JQ0KICBhcnJhbmdlKGRhdGUpDQoNCmNyaW1lX2RhaWx5ICU+JSANCiAgZ2dwbG90KGFlcyhkYXRlLCBjb3VudCkpKyAgDQogIGdlb21fbGluZShjb2xvciA9ICJvcmFuZ2UiKSsNCiAgZ2VvbV9zbW9vdGgoY29sb3IgPSAiYmxhY2siKSANCmBgYA0KDQpUaGUgY3JpbWUgcmF0ZSBhcHBlYXJzIHRvIHJhaXNlIGFyb3VuZCB0aGUgc3VtbWVyIG1vbnRocyBhbmQgbG93ZXJzIGR1cmluZyB0aGUgY29vbGVyIG1vbnRocy4gTGV0cyB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgdGhpcy4NCmBgYHtyfQ0KY3JpbWUgJT4lIA0KICBncm91cF9ieShtb250aCkgJT4lIA0KICBzdW1tYXJpemUoY291bnQ9IG4oKSkgJT4lIA0KICBhcnJhbmdlKG1vbnRoKSANCmBgYA0KDQpgYGB7cn0NCg0KY3JpbWVfbW9udGggPC0gY3JpbWUgJT4lIA0KICBncm91cF9ieShtb250aCwgeWVhcikgJT4lIA0KICBzdW1tYXJpemUoY291bnQ9IG4oKSkgJT4lIA0KICBhcnJhbmdlKG1vbnRoKQ0KY3JpbWVfbW9udGggJT4lIA0KICBnZ3Bsb3QoYWVzKG1vbnRoLCBjb3VudCwgZ3JvdXAgPSAxKSkrDQogIGdlb21fYXJlYShmaWxsID0gIm9yYW5nZSIpKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIGxpbWl0cyA9IGMoMCwgTkEpKSsNCiAgZmFjZXRfd3JhcCh+eWVhcikNCg0KYGBgDQpUaGVyZSBpcyBhIGRpcCBpbiBGZWJydWFyeSByZXBvcnRlZCBjcmltZSAsIGxldHMgdHJ5IHRvIG5vcm1hbGl6ZSB0aGUgZGF0YSB0byBhY2NvdW50IGZvciB0aGUgZmV3ZXIgZGF5cy4gDQpgYGB7cn0NCmNyaW1lX21vbnRoIDwtY3JpbWUgJT4lIA0KICBtdXRhdGUoZGF5c19pbl9tb250aCA9IGRheXNfaW5fbW9udGgoZGF0ZSkpICU+JSANCiAgZ3JvdXBfYnkobW9udGgsIHllYXIsIGRheXNfaW5fbW9udGgpICU+JSANCiAgc3VtbWFyaXplKGNvdW50PSBuKCkpICU+JSANCiAgbXV0YXRlKGRhaWx5X2NyaW1lX3JhdGUgPSBjb3VudC9kYXlzX2luX21vbnRoKQ0KY3JpbWVfbW9udGggJT4lIA0KICBnZ3Bsb3QoYWVzKG1vbnRoLCBkYWlseV9jcmltZV9yYXRlLCBncm91cCA9IDEpKSsNCiAgZ2VvbV9hcmVhKGZpbGwgPSAib3JhbmdlIikrDQogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLCBsaW1pdHMgPSBjKDAsIE5BKSkrDQogIGZhY2V0X3dyYXAofnllYXIpDQpgYGANCkZyb20gaGVyZSB3ZSBjYW4gc2VlIHRoYXQgYWNjb3VudGluZyBmb3IgdGhlIGRlY3JlYXNlZCBvciBpbmNyZWFzZWQgYW1vdW50IG9mIGRheXMgaW4gYSBtb250aCBieSBjcmVhdGluZyBhIGRhaWx5IGNyaW1lIHJhdGUgdmFyaWFibGUgd2UgY2FuIGFjY2VzcyB0aGUgdHJlbmRzIG1vcmUgYWNjdXJhdGVseS4gVGhlIGNyaW1lIHJhdGUgc3RpbGwgbG9va3MgdG8gcmlzZSBkdXJpbmcgdGhlIHdhcm1lciBtb250aHMsIHRlbXBlcmF0dXJlIGRhdGEgaXMgcmVxdWlyZWQgdG8gYW5hbHl6ZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBjcmltZSBhbmQgdGVtcGVyYXR1cmUuDQoNCg0KIyMgQ3JpbWUgVnMuIFRlbXBlcmF0dXJlDQpUaGUgTmF0aW9uYWwgQ2VudGVycyBGb3IgRW52aXJvbm1lbnRhbCBJbmZvcm1hdGlvbihOQ0VJKSBoYXMgaGlzdG9yaWNhbCB3ZWF0aGVyIGFuZCBjbGltYXRlIGRhdGEgZm9yIHRoZSBVbml0ZWQgU3RhdGVzLiBXZSBhcmUgdXNpbmcgYSBkYXRhYmFzZSB0aGF0IHNob3dzIHRoZSBjbGltYXRlIGRhdGEgaW4gRGV0cm9pdCB0YWtlbiBmcm9tIHRoZSBDb2xlbWFuIEEuIFlvdW5nIEludGVybmF0aW9uYWwgQWlycG9ydC4NCiMjIyBJbXBvcnRpbmcgYW5kIEpvaW5pbmcgdGhlIERhdGFzZXRzDQpgYGB7cn0NCndlYXRoZXJfcmF3IDwtIHJlYWQuY3N2KCJEZXRyb2l0X1dlYXRoZXIuY3N2IikNCmRhdGEudGFibGUod2VhdGhlcl9yYXcpDQpgYGANClRoZXJlIGlzIGEgbG90IG9mIHZhcmlhYmxlcyBpbiB0aGVzZSBvYnNlcnZhdGlvbnMuIFdlIG9ubHkgbmVlZCB0aGUgZGF0ZSBhbmQgdGhlIHRlbXBlcmF0dXJlLiBEcnkgYnVsYiB0ZW1wZXJhdHVyZSBha2EgYWlyIHRlbXBlcmF0dXJlIGlzIGRlZmluZWQgYXMgdGhlIHRlbXBlcmF0dXJlIG9mIHRoZSBhaXIgd2hlbiBhIHRoZXJtb21ldGVyIGlzIGV4cG9zZWQgdG8gaXQsIHdlIHdpbGwgYmUgdXNpbmcgdGhpcyB0ZW1wZXJhdHVyZSBmb3Igb3VyIGFuYWx5c2lzLg0KDQpgYGB7cn0NCndlYXRoZXIgPC0gd2VhdGhlcl9yYXcgJT4lIA0KICBzZWxlY3QoREFURSwgSG91cmx5RHJ5QnVsYlRlbXBlcmF0dXJlKSAlPiUNCiAgcmVuYW1lKHRlbXBlcmF0dXJlID0gSG91cmx5RHJ5QnVsYlRlbXBlcmF0dXJlLCBkYXRlID0gREFURSkNCiAgDQp3ZWF0aGVyIDwtIHdlYXRoZXIgJT4lIG11dGF0ZShkYXRlID0geW1kX2htcyhkYXRlKSkNCndlYXRoZXIkZGF0ZSA8LSByb3VuZF9kYXRlKHdlYXRoZXIkZGF0ZSwgdW5pdD0iMTUgbWludXRlcyIpIA0KDQoNCndlYXRoZXIkaG91cl9vZl9kYXkgPC0gaG91cih3ZWF0aGVyJGRhdGUpDQp3ZWF0aGVyJGRhdGUgPC0gZGF0ZSh3ZWF0aGVyJGRhdGUpDQp3ZWF0aGVyIDwtIHVuaXF1ZSh3ZWF0aGVyW2MoImhvdXJfb2ZfZGF5IiwgImRhdGUiLCAidGVtcGVyYXR1cmUiKV0pICANCndlYXRoZXIgPC0gd2VhdGhlclshKGlzLm5hKHdlYXRoZXIkdGVtcGVyYXR1cmUpIHwgd2VhdGhlciR0ZW1wZXJhdHVyZT09IiIpLCBdDQp3ZWF0aGVyJHRlbXBlcmF0dXJlID0gYXMuaW50ZWdlcih3ZWF0aGVyJHRlbXBlcmF0dXJlKQ0KDQpkYXRhLnRhYmxlKHdlYXRoZXIpDQoNCmBgYA0KRm9ybWF0dGVkIGFuZCBtYW5pcHVsYXRlZCB0aGUgZGF0YSBzbyB0aGUgY29sdW1ucyBjYW4gbWF0Y2ggdXAgd2l0aCB0aGUgb3RoZXIgZGF0YSBmcmFtZSBzbyB3ZSBjYW4gam9pbi4gQ3JlYXRpbmcgYSBuZXcgdmFyaWFibGUgY2FsbGVkIGhvdXJfb2ZfZGF5IGFuZCBpdCByb3VuZHMgdGhlIHRpbWUgdXAgb3IgZG93biB0byB0aGUgbmVhcmVzdCAxNSBtaW51dGVzLldlIHdpbGwgYmUgYWJsZSB0byBhbmFseXplIHdoYXQgdGhlIHRlbXBlcmF0dXJlIHdhcyB0aGUgaG91ciB0aGUgY3JpbWUgd2FzIGNvbW1pdHRlZC4NCg0KYGBge3J9DQpjcmltZSA8LSBsZWZ0X2pvaW4oY3JpbWUsIHdlYXRoZXIsIGJ5ID0gYygiZGF0ZSIsICJob3VyX29mX2RheSIpKSAlPiUgICANCiAgZGlzdGluY3QoYXJyZXN0X2NoYXJnZSwgb2ZmZW5zZV9jYXRlZ29yeSwgZGF0ZSwgY3JpbWVfaWQsIC5rZWVwX2FsbCA9IFRSVUUpICU+JSANCiAgc2VsZWN0KC1hcnJlc3RfY2hhcmdlLCAtY3JpbWVfaWQpICU+JSANCiAgZmlsbCh0ZW1wZXJhdHVyZSkNCmRhdGEudGFibGUoY3JpbWUsIG9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSA1LCBzY3JvbGxYID0gJzQwMHB4JykpDQpgYGANCkFmdGVyIGpvaW5pbmcgdGhlIHRhYmxlcyB0aGVyZSB3ZXJlIHNvbWUgTkEgdmFsdWVzIGJlY2F1c2Ugc29tZSBvZiB0aGUgdmFsdWVzIGluIHRoZSB3ZWF0aGVyIGRhdGFzZXQgd2FzIGJsYW5rIHNvIHdlIGp1c3QgZmlsbGVkIGl0IHdpdGggdGhlIG5lYXJlc3QgdmFsdWUgdXNpbmcgdGhlIGZpbGwgZnVuY3Rpb24uIA0KDQpgYGB7cn0NCmNyaW1lX3RlbXBfbW9udGggPC0gY3JpbWUgJT4lIA0KICBtdXRhdGUocG9wID0gY2FzZV93aGVuKA0KICAgIHllYXIgPT0gMjAxNyB+IDY3OTg2NSwNCiAgICB5ZWFyID09IDIwMTggfiA2NzcxNTUsDQogICAgeWVhciA9PSAyMDE5IH4gNjc0ODQxLA0KICAgIHllYXIgPT0gMjAyMCB+IDY2NDEzOQ0KICApKSAlPiUgDQogIGdyb3VwX2J5KG1vbnRoLCB5ZWFyLCBwb3ApICU+JQ0KICBzdW1tYXJpemUobWVhbl90ZW1wID0gbWVhbih0ZW1wZXJhdHVyZSwgbmEucm0gPVQpLCBjb3VudCA9IG4oKSkgJT4lIA0KICBtdXRhdGUoY3JpbWVfcmF0ZSA9IGNvdW50L3BvcCoxMDAwMDApDQoNCmNyaW1lX3RlbXBfbW9udGggJT4lIA0KICBnZ3Bsb3QoYWVzKG1lYW5fdGVtcCwgY3JpbWVfcmF0ZSkpICsgDQogIGdlb21fcG9pbnQoKSsNCiAgZ2VvbV9zbW9vdGgoY29sb3IgPSAib3JhbmdlIiwgbWV0aG9kID0gImxtIikrDQogIGxhYnModGl0bGUgPSAiVGVtcGVyYXR1cmUgdnMuIENyaW1lIFJhdGUgaW4gRGV0cm9pdCgyMDE3LTIwMjApIiwgeCA9ICIgQXZlcmFnZSBUZW1wZXJhdHVyZShNb250aCkiLCB5ID0gIkNyaW1lIFJhdGUgUGVyIDEwMDAwMCIpDQoNCmBgYA0KSGVyZSB3ZSBjYW4gc2VlIHRoZSBhdmVyYWdlIHRlbXBlcmF0dXJlIG9mIGFsbCB0aGUgbW9udGhzIGFuZCB0aGUgY3JpbWUgcmF0ZSBwZXIgMTAwMDAwLiBXZSBjYW4gc2VlIGEgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgYXZlcmFnZSB0ZW1wZXJhdHVyZSBhbmQgdGhlIGNyaW1lIHJhdGUuDQpgYGB7cn0NCmNvcihjcmltZV90ZW1wX21vbnRoJG1lYW5fdGVtcCwgY3JpbWVfdGVtcF9tb250aCRjcmltZV9yYXRlKQ0KYGBgDQpXZSBjYW4gc2VlIHRoYXQgdGhlIGNvcnJlbGF0aW9uIGlzIC44IHdoaWNoIGlzIGEgc3Ryb25nIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMuIA0KIyMgQ3JpbWUgVnMuIERheSBvZiBXZWVrDQoNCmBgYHtyfQ0KaG91cl9mb3JtYXQgPC0gYyhwYXN0ZShjKDEyLDE6MTEpLCJBTSIpLCBwYXN0ZShjKDEyLDE6MTEpLCJQTSIpKQ0KZG93X2Zvcm1hdCA8LSBjKCJTdW5kYXkiLCJNb25kYXkiLCJUdWVzZGF5IiwiV2VkbmVzZGF5IiwiVGh1cnNkYXkiLCJGcmlkYXkiLCJTYXR1cmRheSIpDQoNCmNyaW1lX2RvdyA8LSBjcmltZSAlPiUgDQogIGdyb3VwX2J5KGRheV9vZl93ZWVrLCBvZmZlbnNlX2NhdGVnb3J5LCBob3VyX29mX2RheSkgJT4lIA0KICBzdW1tYXJpemUoY291bnQgPSBuKCkpDQoNCmNyaW1lX2RvdyRkYXlfb2Zfd2VlayA8LSBmYWN0b3IoY3JpbWVfZG93JGRheV9vZl93ZWVrLCBsYWJlbHMgPSByZXYoZG93X2Zvcm1hdCksIG9yZGVyZWQgPSBUUlVFKQ0KY3JpbWVfZG93JGhvdXJfb2ZfZGF5IDwtIGZhY3RvcihjcmltZV9kb3ckaG91cl9vZl9kYXksIGxldmVsID0gMDoyMywgbGFiZWwgPSBob3VyX2Zvcm1hdCkNCg0KDQpkYXRhLnRhYmxlKGNyaW1lX2RvdykNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChjcmltZV9kb3csIGFlcyh4ID0gaG91cl9vZl9kYXksIHkgPSBkYXlfb2Zfd2VlaywgZmlsbCA9IGNvdW50KSkrDQogIGdlb21fdGlsZSgpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNiksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uPSJ0b3AiLCBsZWdlbmQuZGlyZWN0aW9uPSJob3Jpem9udGFsIiwgbGVnZW5kLmtleS53aWR0aD11bml0KDIsICJjbSIpLCBsZWdlbmQua2V5LmhlaWdodD11bml0KDAuMjUsICJjbSIpKSArDQogIGxhYnMoeCA9ICJIb3VyIG9mIFJlcG9ydGVkIENyaW1lIiwgeSA9ICJEYXkgb2YgV2VlayAiLCB0aXRsZSA9ICJSZXBvcnRlZCBQcm9wZXJ0eSBhbmQgVmlvbGVudCBDcmltZXMgaW4gRGV0cm9pdCAoMjAxNy0yMDIwKSIpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLCBoaWdoID0gIiMxMDFhZGUiKQ0KYGBgDQoNCkxldHMgbG9vayBhdCB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBvZmZlbnNlIGNhdGVnb3J5IGFuZCB0aW1lLg0KDQoNCmBgYHtyfQ0KY3JpbWVfZG93IDwtIGNyaW1lX2RvdyAlPiUgDQogIG11dGF0ZShub3JtID0gY291bnQvc3VtKGNvdW50KSkNCmdncGxvdChjcmltZV9kb3csIGFlcyh4ID0gaG91cl9vZl9kYXksIHkgPSBkYXlfb2Zfd2VlaywgZmlsbCA9IG5vcm0pKSArDQogIGdlb21fdGlsZSgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjYsIHNpemUgPSA0KSkgKw0KICBsYWJzKHggPSAiSG91ciBvZiBDcmltZSIsIHkgPSAiRGF5IG9mIFdlZWsiLCB0aXRsZSA9ICJOdW1iZXIgb2YgQ3JpbWVzIGluIERldHJvaXQgZnJvbSAyMDE3LTIwMjAgYnkgTm9ybWFsaXplZCBieSBPZmZlbnNlIikgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAiIzEwMWFkZSIpICsNCiAgZmFjZXRfd3JhcCh+IG9mZmVuc2VfY2F0ZWdvcnkpDQpgYGANCkhlcmUgd2UgY2FuIHNlZSB0aGF0IGEgbG90IG9mIGNyaW1lcyBoYXBwZW4gbGF0ZSBuaWdodC9lYXJseSBtb3JuaW5nLg0KDQojIyBDcmltZSBieSBaaXBjb2RlDQpMZXRzIHRha2UgYSBsb29rIGF0IGNyaW1lIGJ5IHppcCBjb2RlDQoNCkZhY3RvcmluZy4NCmBgYHtyfQ0KY3JpbWVfZG93X3ppcGNvZGUgPC0gY3JpbWUgJT4lIA0KICBncm91cF9ieShkYXlfb2Zfd2VlaywgemlwX2NvZGUsIGhvdXJfb2ZfZGF5KSAlPiUgDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkNCg0KY3JpbWVfZG93X3ppcGNvZGUkZGF5X29mX3dlZWsgPC0gZmFjdG9yKGNyaW1lX2Rvd196aXBjb2RlJGRheV9vZl93ZWVrLCBsYWJlbHMgPSByZXYoZG93X2Zvcm1hdCksIG9yZGVyZWQgPSBUUlVFKQ0KY3JpbWVfZG93X3ppcGNvZGUkaG91cl9vZl9kYXkgPC0gZmFjdG9yKGNyaW1lX2Rvd196aXBjb2RlJGhvdXJfb2ZfZGF5LCBsZXZlbCA9IDA6MjMsIGxhYmVsID0gaG91cl9mb3JtYXQpDQpgYGANCmBgYHtyLCBmaWcuaGVpZ2h0ID0gMTIsIGZpZy53aWR0aCA9IDEyfQ0KZ2dwbG90KGNyaW1lX2Rvd196aXBjb2RlLCBhZXMoeCA9IGhvdXJfb2ZfZGF5LCB5ID0gZGF5X29mX3dlZWssIGZpbGwgPSBjb3VudCkpICsNCiAgZ2VvbV90aWxlKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNiwgc2l6ZSA9IDQpKSArDQogIGxhYnMoeCA9ICJIb3VyIG9mIENyaW1lIiwgeSA9ICJEYXkgb2YgV2VlayIsIHRpdGxlID0gIk51bWJlciBvZiBjcmltZXMgaW4gRGV0cm9pdCAyMDE3IOKAkyAyMDIwLCBieSBaaXBjb2RlIikgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIGhpZ2ggPSAiIzEwMWFkZSIpICsNCiAgZmFjZXRfd3JhcCh+IHppcF9jb2RlKSAgDQpgYGANClRoaXMgdmlzdWFsaXphdGlvbiBkb2Vzbid0IGFjY291bnQgZm9yIGNyaW1lIGluIHppcCBjb2RlcyB3aGVyZSB0aGUgcG9wdWxhdGlvbiBpbiBsb3dlci4gTGV0cyBmaW5kIHNvbWUgZGF0YSBvbiBwb3B1bGF0aW9uIGluIHRoZSBhcmVhIGNvZGVzIHNvIHdlIGNhbiBsb29rIGF0IGNyaW1lIHJhdGUuIA0KDQpgYGB7cn0NCnppcF9jb2RlX2RhdGFiYXNlPC0gcmVhZC5jc3YoImRldHJvaXRfemlwY29kZXMuY3N2IikNCmRhdGEudGFibGUoemlwX2NvZGVfZGF0YWJhc2UpDQoNCg0KYGBgDQpUaGlzIGRhdGEgaXMgZnJvbSBodHRwczovL3d3dy5taWNoaWdhbi1kZW1vZ3JhcGhpY3MuY29tLyB3aGljaCBjbGFpbXMgdG8gaGF2ZSB1cCB0byBkYXRlIGRlbW9ncmFwaGljcyBmcm9tIHRoZSBVUyBjZW5zdXMuU29tZSBvZiB0aGUgemlwIGNvZGVzIGluIHRoZSBkYXRhc2V0IGhhdmUgb3RoZXIgY2l0aWVzIGluIHdoaWNoIHdlIGRvbnQgaGF2ZSBjcmltZSBkYXRhIGZvci4gU28gdGhvc2UgemlwY29kZXMgd2lsbCBiZSBmaWx0ZXJlZCBvdXQgc28gd2UgY2FuIGxvb2sgYXQgYSBtYWpvcml0eSBvZiBEZXRyb2l0LiAgDQpgYGB7cn0NCg0KZmlsdGVyZWRfemlwY29kZXMgPC1jKDQ4MjAzLCA0ODIxMiwgNDgyMzYsIDQ4MjM5LCA0ODI0MykNCg0KY3JpbWVfemlwY29kZSA8LSBjcmltZSAlPiUgDQogIGZpbHRlcih6aXBfY29kZSAlbm90aW4lIGZpbHRlcmVkX3ppcGNvZGVzKSAlPiUgDQogIGdyb3VwX2J5KHppcF9jb2RlLCBjcmltZV90eXBlKSAlPiUgDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkNCg0KZGF0YS50YWJsZSh6aXBfY29kZV9kYXRhYmFzZSkNCg0KYGBgDQpgYGB7cn0NCmNyaW1lX3ppcGNvZGUgPC0gbGVmdF9qb2luKGNyaW1lX3ppcGNvZGUsIHppcF9jb2RlX2RhdGFiYXNlLCBieSA9ICJ6aXBfY29kZSIpICU+JSANCiAgZHJvcF9uYSgpICU+JSANCiAgbXV0YXRlKGNyaW1lX3JhdGUgPSAoY291bnQvUG9wdWxhdGlvbikqMTAwMCkNCmNyaW1lX3ppcGNvZGUkemlwX2NvZGUgPC0gZmFjdG9yKGNyaW1lX3ppcGNvZGUkemlwX2NvZGUpDQoNCg0KZGF0YS50YWJsZShjcmltZV96aXBjb2RlKQ0KYGBgDQpgYGB7cn0NCmdncGxvdChjcmltZV96aXBjb2RlLCBhZXMoemlwX2NvZGUsY3JpbWVfcmF0ZSwgZmlsbCA9IGNyaW1lX3R5cGUpKSsNCiAgZ2VvbV9jb2woKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKCJjcmltZV90eXBlIiwgdmFsdWVzID0gYygicHJvcGVydHkiPSAiIzEwMWFkZSIsICJ2aW9sZW50IiA9ICJvcmFuZ2UiKSkgKw0KICBjb29yZF9mbGlwKCkrDQogIGxhYnModGl0bGUgPSAiQ3JpbWUgUmF0ZSBpbiBEZXRyb2l0IGJ5IFppcGNvZGUgKDIwMTctMjAyMCkiLHggPSAiWmlwIENvZGUiLA0KICAgICAgIHkgPSAiQ3JpbWUgUmF0ZSBwZXIgMTAwMCIsDQogICAgICAgZmlsbCA9ICJDcmltZSBUeXBlIikNCmBgYA0KSGVyZSB3ZSBjYW4gc2VlIHRoZSBjcmltZSBieSB6aXAgY29kZXMgYW5kIHdoYXQgaW5zdGFudGx5IHN0YW5kcyBvdXQgaXMgdGhlIDQ4MjI2IHppcCBjb2RlLiBUaGlzIHppcCBjb2RlIGlzIGRvd250b3duIGFuZCBoYXMgdmVyeSBmZXcgcmVzaWRlbnRzIHNvIHRoZSBjcmltZSByYXRlIGlzIGluZmxhdGVkLiBUaGUgY3JpbWUgcmF0ZSBpcyBhbHNvIG1pc2xlYWRpbmcgYmVjYXVzZSBkb3dudG93biBEZXRyb2l0IGlzIGEgaG90IHNwb3Qgb2YgbWFueSBhdHRyYWN0aW9ucyBhbmQgdGhhdCBsZWFkcyB0byBub24gcmVzaWRlbnRzIGZyb20gYWxsIGFyb3VuZCB0aGUgTWV0cm8gRGV0cm9pdCBhcmVhIHRvIHZpc2l0LiBUaGVyZSBpcyBvdGhlciB6aXBjb2RlcyB0aGF0IGFyZSBuZXcgZG93bnRvd24gb3IgbWlkdG93biBJIHdpbGwgZ2V0IGV4Y2x1ZGUgZnJvbSB0aGlzIGFuYWx5c2lzLiAgDQoNClRoaXMgZ3JhcGggaXMgc2NhbGVkIGJldHRlciBhbmQgd2UgY2FuIHNlZSB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgb3RoZXIgemlwY29kZXMuDQpgYGB7cn0NCmRvd250b3duX3ppcGNvZGVzIDwtIGMoNDgyNDMsIDQ4MjI2LCA0ODIwMSwgNDgyMDIsIDQ4MjE2LCA0ODIwNykNCmNyaW1lX3ppcGNvZGUgJT4lIA0KICBmaWx0ZXIoemlwX2NvZGUgJW5vdGluJSBkb3dudG93bl96aXBjb2RlcykgJT4lIA0KICBnZ3Bsb3QoYWVzKHppcF9jb2RlLGNyaW1lX3JhdGUsIGZpbGwgPSBjcmltZV90eXBlKSkrDQogIGdlb21fY29sKCkgKw0KICBzY2FsZV9maWxsX21hbnVhbCgiY3JpbWVfdHlwZSIsIHZhbHVlcyA9IGMoInByb3BlcnR5Ij0gIiMxMDFhZGUiLCAidmlvbGVudCIgPSAib3JhbmdlIikpICsNCiAgY29vcmRfZmxpcCgpKw0KICBsYWJzKHRpdGxlID0gIkNyaW1lIFJhdGUgaW4gRGV0cm9pdCBieSBaaXBjb2RlKiAoMjAxNy0yMDIwKSIsDQogICAgICAgY2FwdGlvbiA9ICIqIERvd250b3duLCBNaWR0b3duIGFuZCBzdXJyb3VuZGluZyBhcmVhcyByZW1vdmVkIiwNCiAgICAgICB4ID0gIlppcCBDb2RlIiwNCiAgICAgICB5ID0gIkNyaW1lIFJhdGUgcGVyIDEwMDAiLA0KICAgICAgIGZpbGwgPSAiQ3JpbWUgVHlwZSIpDQpgYGANCg0KDQoNCiMjIENvbmNsdXNpb24gDQoNCldlIGNhbiBzZWUgdGhhdCB3ZWF0aGVyIGhhcyBhIHBvc2l0aXZlIGNvcnJlbGF0aW9uIHdpdGggY3JpbWUgcmF0ZSBhbHNvIGl0IHNlZW1zIGxpa2UgdGhlIHN1bW1lciBtb250aHMgYXJlIHdoZW4gdGhlIGNyaW1lIHJhdGUgaXMgdGhlIGhpZ2hlc3QgYW5kIHRoZSBjb2xkZXIgbW9udGhzIGVzcGVjaWFsbHkgRmVicnVhcnkgc2VlaW5nIGEgZGVjbGluZS4gTW9zdCBvZiB0aGUgY3JpbWVzIGhhcHBlbiBkdXJpbmcgbGF0ZSBuaWdodC8gZWFybHkgbW9ybmluZy4gV2UgY2FuIGFsc28gc2VlIGEgY29ycmVsYXRpb24gd2l0aCBDb3ZpZC0xOSBhbmQgY3JpbWUgc2luY2UgMjAyMCBudW1iZXJzIHdlcmUgbG93ZXIgdGhhbiB0aGUgb3RoZXIgeWVhcnMuIFdlIHRoZW4gdG9vayBhIGxvb2sgYXQgdGhlIGNyaW1lIHJhdGUgYnkgemlwY29kZSBhbmQgbG9va2VkIGF0IHRoZSB0aW1lIGFuZCBkYXkgd2hlbiBhIGNlcnRhaW4gb2ZmZW5zZSBtaWdodCBiZSBjb21taXR0ZWQu